package shop.up.cart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import shop.up.cart.mapper.CartInfoMapper;
import shop.up.cart.service.CartAsyncService;
import shop.up.cart.service.CartService;
import shop.up.common.constant.RedisConst;
import shop.up.common.util.DateUtil;
import shop.up.model.cart.CartInfo;
import shop.up.model.product.SkuInfo;
import shop.up.product.client.ProductFeignClient;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 购物车业务接口实现 {@link CartServiceImpl}
 *
 * @author Kevin
 * @date 2022/7/25
 */
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    private CartInfoMapper cartInfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private CartAsyncService cartAsyncService;

    /**
     * 添加购物车
     * @param skuId 商品id
     * @param userId 用户id
     * @param skuNum 商品件数
     */
    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        // 1.添加商品之前，先看一下购物车中是否有该商品
        //   true: 商品数量相加
        //   false: 直接加入购物车
        // 2.将数据同步到redis！

        // 获取购物车key
        String cartKey = getCartKey(userId);
        if (!redisTemplate.hasKey(cartKey)) {
            // 从数据库加载，并写入缓存
            loadCartCache(userId);
        }
        // 从缓存中查询
        CartInfo cartInfoExist  = (CartInfo)redisTemplate.boundHashOps(cartKey).get(skuId.toString());
        if (cartInfoExist != null) { // 已经存在的商品
            // 修改商品数量
            cartInfoExist.setSkuNum(cartInfoExist.getSkuNum() + skuNum);
            BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
            cartInfoExist.setSkuPrice(skuPrice);
            cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
            cartInfoExist.setIsChecked(1);

            // 更新数据库
            // cartInfoMapper.updateById(cartInfoExist);
            cartAsyncService.updateCartInfo(cartInfoExist);

        } else { // 新商品添加到购物车
            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            cartInfo.setSkuNum(skuNum);
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            cartInfo.setCartPrice(skuInfo.getPrice());
            cartInfo.setSkuPrice(skuInfo.getPrice());
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
            cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));

            // 新增数据库
            // cartInfoMapper.insert(cartInfo);
            cartAsyncService.saveCartInfo(cartInfo);

            cartInfoExist = cartInfo;
        }

        // 同时写入缓存
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(), cartInfoExist);
        // 设置缓存过期时间
        setCartKeyExpire(cartKey);

    }

    /**
     * 查询购物车列表
     * @param userId
     * @param userTempId
     * @return
     */
    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        List<CartInfo> cartInfoList = new ArrayList<>();

        // 未登录：临时用户Id 获取未登录的购物车数据
        if (StringUtils.isEmpty(userId)) {
            cartInfoList = getCartList(userTempId);
            return cartInfoList;
        }

        //已登录
        if (!StringUtils.isEmpty(userId)) {
            // 查询临时用户的购物车
            List<CartInfo> cartInfoArrayList = getCartList(userTempId);
            if (!CollectionUtils.isEmpty(cartInfoArrayList)) {
                // 如果未登录购物车中有数据，则进行合并 合并的条件：skuId 相同
                cartInfoList = mergeToCartList(cartInfoArrayList, userId);
                // 删除未登录购物车数据
                deleteCartList(userTempId);
            }

            // 如果未登录购物车中没有数据！
            if (StringUtils.isEmpty(userTempId) || CollectionUtils.isEmpty(cartInfoArrayList)) {
                // 根据什么查询？userId
                cartInfoList = this.getCartList(userId);
            }
        }

        return cartInfoList;
    }

    // 查询购物车
    private List<CartInfo> getCartList(String userId) {
        // 声明一个返回的集合对象
        List<CartInfo> cartInfoList = new ArrayList<>();
        if (StringUtils.isEmpty(userId)){
            return cartInfoList;
        }

        // 1.  根据用户Id 查询 {先查询缓存，缓存没有，再查询数据库}
        // 定义key user:userId:cart
        String cartKey = getCartKey(userId);
        // 获取数据
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            // 购物车列表显示有顺序：按照商品的更新时间 降序
            cartInfoList.sort(new Comparator<CartInfo>() {
                @Override
                public int compare(CartInfo o1, CartInfo o2) {
                    return DateUtil.truncatedCompareTo(o2.getUpdateTime(),o1.getUpdateTime(),Calendar.SECOND);
                }
            });
            return cartInfoList;
        } else {
            // 缓存中没有数据！查询数据库
            cartInfoList = loadCartCache(userId);
            return cartInfoList;
        }
    }

    // 从数据库中查询数据，并写入缓存
    public List<CartInfo> loadCartCache(String userId) {
        // 从数据库中查询
        QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId).orderByDesc("update_time");
        List<CartInfo> cartInfoList = cartInfoMapper.selectList(queryWrapper);

        if (CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList;
        }

        // 当缓存中的数据为空时，购物车中的数据可能发生变动，需要查询商品实时价格
        HashMap<String, CartInfo> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfoList) {
            BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfo.getSkuId());
            // 更新sku的价格
            cartInfo.setSkuPrice(skuPrice);
            map.put(cartInfo.getSkuId().toString(), cartInfo);
        }

        // 写缓存
        // 定义key user:userIed:cart
        String cartKey = getCartKey(userId);
        // 批量添加
        redisTemplate.opsForHash().putAll(cartKey, map);
        // 设置过期时间
        this.setCartKeyExpire(cartKey);

        return cartInfoList;
    }

    /**
     * 更新勾选状态
     * @param userId
     * @param isChecked
     * @param skuId
     */
    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        // 异步更新数据库
        cartAsyncService.checkCart(userId, isChecked, skuId);

        // 更新缓存
        String cartKey = getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())) {
            CartInfo cartInfo = hashOperations.get(skuId.toString());
            cartInfo.setIsChecked(isChecked);
            hashOperations.put(skuId.toString(), cartInfo);
            setCartKeyExpire(cartKey);
        }

    }

    /**
     * 删除购物车中的商品
     * @param skuId
     * @param userId
     */
    @Override
    public void deleteCart(Long skuId, String userId) {
        // 数据库异步删除
        cartAsyncService.deleteCartInfo(userId, skuId);

        // 缓存删除
        String cartKey = getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())){
            hashOperations.delete(skuId.toString());
        }
    }

    /**
     * 删除临时用户购物车
     * @param userTempId
     */
    private void deleteCartList(String userTempId) {
        // 删除数据库，删除缓存
        // 异步删除
        cartAsyncService.deleteCartInfo(userTempId);

        String cartKey = getCartKey(userTempId);
        Boolean flag = redisTemplate.hasKey(cartKey);
        if (flag){
            redisTemplate.delete(cartKey);
        }
    }

    /**
     * 合并购物车
     * @param cartInfoNoLoginList
     * @param userId
     * @return
     */
    private List<CartInfo> mergeToCartList(List<CartInfo> cartInfoNoLoginList, String userId) {
        // 查询登录用户购物车
        List<CartInfo> cartInfoLoginList = getCartList(userId);

        Map<Long, CartInfo> longCartInfoMap = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
        // 判断临时用户购物车中的商品id是否和登录用户商品id相同
        for (CartInfo cartInfo : cartInfoNoLoginList) {
            Long skuId = cartInfo.getSkuId();
            // 若相同，则更新购物车
            if (longCartInfoMap.containsKey(skuId)){
                //  未登录购物车中的商品，在登录中也存在！数量相加
                //  用这个skuId 对应获取登录的cartInfo 对象
                CartInfo cartInfoLogin = longCartInfoMap.get(skuId);
                //  赋值商品数量
                cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum()+cartInfo.getSkuNum());
                //  更新一下cartInfo 的更新时间
                cartInfoLogin.setUpdateTime(new Timestamp(new Date().getTime()));

                //  合并购物车时的选择状态！
                //  以未登录选中状态为基准
                if (cartInfo.getIsChecked().intValue() == 1){
                    cartInfoLogin.setIsChecked(1);
                }
                //  更新数据库 同步更新
                //  cartInfoMapper.updateById(cartInfoLogin);
                //  因为：合并的时候，缓存有数据的话，缓存中cartInfo.id 为null，使用updateById更新失败。
                //  使用 update cart_info set sku_num = ? where sku_id = ? and user_id = ?
                QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("user_id",cartInfoLogin.getUserId());
                queryWrapper.eq("sku_id",cartInfoLogin.getSkuId());
                cartInfoMapper.update(cartInfoLogin, queryWrapper);
                //  异步更新 代码不会走这个方法体！意味着不会更新数据库
                //  cartAsyncService.updateCartInfo(cartInfoLogin);
            }else {
                // 若没有，则新增购物车
                //  赋值登录的用户id
                cartInfo.setUserId(userId);
                //  添加时间
                cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                cartInfoMapper.insert(cartInfo);
                //  代码不会走这个方法体！意味着不会更新数据库
                //  cartAsyncService.saveCartInfo(cartInfo);
            }
        }
        // 调用 loadCartCache 再次查询该用户的最新购物车，并存入缓存
        List<CartInfo> cartInfoList = loadCartCache(userId);

        return cartInfoList;
    }

    // 设置过期时间
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    // 获取购物车的key
    private String getCartKey(String userId) {
        //定义key user:userId:cart
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }

    /**
     * 查询选择的商品
     * @param userId
     * @return
     */
    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        // 先从缓存查询
        String cartKey = getCartKey(userId);
        List<CartInfo> cartInfoCacheList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartInfoCacheList)) {
            cartInfoList = cartInfoCacheList.stream()
                    .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                    .collect(Collectors.toList());
        } else {
            // 从数据库查询
            QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId);
            queryWrapper.eq("is_checked", 1);
            cartInfoList = cartInfoMapper.selectList(queryWrapper);
        }
        return cartInfoList;
    }

}
